package com.newgrand.annexserver.storage.service;

import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import com.mongodb.client.gridfs.GridFSFindIterable;
import com.mongodb.client.gridfs.GridFSUploadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.mongodb.client.gridfs.model.GridFSUploadOptions;
import com.mongodb.client.model.Filters;
import com.newgrand.annexcommon.model.FileEntity;
import com.newgrand.annexcommon.utils.FileUtil;
import com.newgrand.annexserver.beans.Dbconfig;
import com.newgrand.annexserver.storage.StorageContextHolder;
import org.apache.commons.io.FileUtils;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class MongoStorage implements StorageService {

    private final static Logger logger = LoggerFactory.getLogger(MongoStorage.class);

    private Dbconfig dbconfig;

    private MongoClient mongoClient;
    private GridFSBucket gridFSBucket;

    public MongoStorage(Dbconfig dbconfig) {
        if (mongoClient != null) {
            close();
        }
        this.dbconfig = dbconfig;
        open();
    }

    public String save(FileEntity entity) {
        String fileName = entity.getFileName();
        logger.info("save mongo {}", fileName);

        String fileId = null;
        GridFSUploadOptions options = new GridFSUploadOptions();
        try (GridFSUploadStream gfsUpload = gridFSBucket.openUploadStream(fileName, options)) {
            byte[] data = entity.getData();
            gfsUpload.write(data);
            gfsUpload.flush();
            fileId = gfsUpload.getObjectId().toString();
        } catch (Exception e) {
            logger.error("save mongo " + fileName, e);
        }
        return fileId;
    }

    public String saveThumb(FileEntity entity) {
        entity.setFileName(FileUtil.getMongoThumbFileName(entity.getFileName()));

        logger.info("save mongo thumb {}", entity.getFileName());
        return save(entity);
    }

    public FileEntity query(String id) {
        logger.info("query mongo {}", id);
        try (ByteArrayOutputStream bao = new ByteArrayOutputStream()) {
            gridFSBucket.downloadToStream(new ObjectId(id), bao);
            byte[] data = bao.toByteArray();
            return new FileEntity(data);
        } catch (Exception e) {
            logger.error("query mongo " + id, e);
        }
        return null;
    }

    public void open() {
        if (dbconfig != null) {
            String urlStr = dbconfig.getStoreHost();
            String[] arr = urlStr.split(",");
            List<ServerAddress> addresses = new ArrayList<ServerAddress>();
            for (String url : arr) {
                String host = url.split(":")[0];
                int port = Integer.parseInt(url.split(":")[1]);
                addresses.add(new ServerAddress(host, port));
            }
            String username = dbconfig.getStoreUsername();
            String databaseStr = dbconfig.getStoreUrl();
            char[] password = dbconfig.getStorePassword().toCharArray();
            MongoCredential credential = MongoCredential.createCredential(username, databaseStr, password);
            mongoClient = MongoClients.create(MongoClientSettings.builder()
                    .applyToClusterSettings(builder -> builder.hosts(addresses))
                    .credential(credential).build());
            MongoDatabase database = mongoClient.getDatabase(databaseStr);
            gridFSBucket = GridFSBuckets.create(database);

            logger.info("open mongo {}", dbconfig);
        }
    }

    public void close() {
        try {
            if (mongoClient != null) {
                mongoClient.close();
            }
        } catch (Exception e) {
            logger.error("close", e);
        }
        mongoClient = null;
    }

    public FileEntity queryThumb(String id) {
        logger.info("query mongo thumb {}", id);
        Bson eq = Filters.eq("_id", new ObjectId(id));
        GridFSFindIterable iterable = gridFSBucket.find(eq);
        GridFSFile dbFile = iterable.first();
        if (dbFile != null && !StringUtils.isEmpty(dbFile.getFilename())) {
            String thumbName = FileUtil.getMongoThumbFileName(dbFile.getFilename());
            try (ByteArrayOutputStream bao = new ByteArrayOutputStream()) {
                gridFSBucket.downloadToStream(thumbName, bao);
                return new FileEntity(bao.toByteArray());
            } catch (Exception e) {
                logger.error("query mongo thumb " + id, e);
            }
        }
        return null;
    }

}
